package org.apereo.cas.web;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.CollectionUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.DefaultCorsProcessor;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


/**
 * @author guhai
 */
public class ExtDefaultCorsProcessor extends DefaultCorsProcessor {

    @Override
    protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response,
                                     CorsConfiguration config, boolean preFlightRequest) throws IOException {

        String requestOrigin = request.getHeaders().getOrigin();
        String allowOrigin = checkOrigin(config, requestOrigin);

        HttpMethod requestMethod = getMethodToUse(request, preFlightRequest);
        List<HttpMethod> allowMethods = checkMethods(config, requestMethod);

        List<String> requestHeaders = getHeadersToUse(request, preFlightRequest);
        List<String> allowHeaders = checkHeaders(config, requestHeaders);

        if (allowOrigin == null || allowMethods == null || (preFlightRequest && allowHeaders == null)) {
            rejectRequest(response);
            return false;
        }

        HttpHeaders responseHeaders = response.getHeaders();
        //fixed by guhai.覆盖Spring的DefaultCorsProcessor，默认的实现会导致浏览器中出现以下错误。目前还没有查清楚Access-Control-Allow-Origin:* 是从哪里写进去的
        //The 'Access-Control-Allow-Origin' header contains multiple values 'http://oldop-dev.xxx.co, *', but only one is allowed.
        responseHeaders.setAccessControlAllowOrigin(allowOrigin);
        responseHeaders.add(HttpHeaders.VARY, HttpHeaders.ORIGIN);

        if (preFlightRequest) {
            responseHeaders.setAccessControlAllowMethods(allowMethods);
        }

        if (preFlightRequest && !allowHeaders.isEmpty()) {
            responseHeaders.setAccessControlAllowHeaders(allowHeaders);
        }

        if (!CollectionUtils.isEmpty(config.getExposedHeaders())) {
            responseHeaders.setAccessControlExposeHeaders(config.getExposedHeaders());
        }

        if (Boolean.TRUE.equals(config.getAllowCredentials())) {
            responseHeaders.setAccessControlAllowCredentials(true);
        }

        if (preFlightRequest && config.getMaxAge() != null) {
            responseHeaders.setAccessControlMaxAge(config.getMaxAge());
        }

        response.flush();
        return true;
    }

    private HttpMethod getMethodToUse(ServerHttpRequest request, boolean isPreFlight) {
        return (isPreFlight ? request.getHeaders().getAccessControlRequestMethod() : request.getMethod());
    }

    private List<String> getHeadersToUse(ServerHttpRequest request, boolean isPreFlight) {
        HttpHeaders headers = request.getHeaders();
        return (isPreFlight ? headers.getAccessControlRequestHeaders() : new ArrayList<String>(headers.keySet()));
    }

    @Override
    protected String checkOrigin(CorsConfiguration config, String requestOrigin) {
        return requestOrigin;
    }
}
